/*
 * Copyright (c) 2006-2021, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2023-05-22     甘泽       the first version
 */

// 此.c文件实现bmm150单独作为一个传感器设备，而非作为bmi160的从设备，注册到rtt的sensor驱动框架中，
// 最后结果是：
// 1）用户调用rt_hw_bmm150_init，将bmm150注册
// 2）随后进行初始化，包括总线选择、通信协议配置、上电模式、输出速率等
// 3）最后用户使用rtt的sensor统一的API接口对传感器bmm150进行open\close\read等操作
//
// 而sensor_bosch_bmx160.c的出发点是将bmm150作为bmi160的从设备，且通过I2C总线进行通信

#include <rtthread.h>
#include <rtdevice.h>
#include <sensor.h>
#include <drv_spi.h>
#include <drivers/i2c.h>
#include <drivers/pin.h>
#include <drivers/spi.h>
#include <bmm150.h>
#include "sensor_bmm150.h"

#define DBG_ENABLE
#define DBG_LEVEL DBG_LOG
#define DBG_SECTION_NAME "sensor.bosch.bmm150"
#define DBG_COLOR
#include <rtdbg.h>

#define FIFO_DATA_LEN 8

static int _get_data(struct rt_sensor_device *sensor, void *buf);

static int _get_fifo_data(struct rt_sensor_device *sensor, void *buf, rt_size_t len);

static rt_size_t _fetch_data(struct rt_sensor_device *sensor, void *buf, rt_size_t len);

static rt_err_t _control(struct rt_sensor_device *sensor, int cmd, void *args);

static struct bmm150_dev *_bmm150_create(struct rt_sensor_intf *intf, uint8_t spi_i2c);

static void *_mag_init(const char *name, struct rt_sensor_config *cfg, void *hdev, uint8_t spi_i2c);

static void _fifo_init(struct bmm150_dev *dev);

static rt_err_t _set_odr(rt_sensor_t sensor, rt_uint16_t odr);

static rt_err_t _set_power(rt_sensor_t sensor, rt_uint8_t power);

static rt_err_t _set_range(rt_sensor_t sensor, rt_uint16_t range);

static void _get_mag_data(struct bmm150_dev *bmm150, struct rt_sensor_data *data);

static struct rt_sensor_ops sensor_ops =
    {
        _fetch_data,
        _control};

static struct rt_spi_device bmm150_spi_dev;

// 此函数的作用是在dev设备结构体的fifo成员指针指向的FIFO信息结构体后面，连接一块7*8字节大小的内存块，用于存储FIFO信息
// 同时fifo信息结构体后面将同步跟踪内存块的信息
static void _fifo_init(struct bmm150_dev *dev)
{
    // empty
}

struct odr_node
{
    rt_uint16_t odr;
    rt_uint16_t mask;
};

static rt_uint16_t range_find_mask(const struct odr_node *tab, rt_uint16_t odr)
{
    const struct odr_node *node = tab;

    for (; node->odr && node->odr <= odr; node++)
        ;
    return node->odr && node != tab ? node[-1].mask : node->mask;
}

static void _delay_ms(uint32_t period)
{
    rt_thread_mdelay(period);
}

static int8_t rt_i2c_write_reg(void *bus, uint8_t addr, uint8_t reg, uint8_t *data, uint16_t len)
{
    rt_uint8_t tmp = reg;
    struct rt_i2c_msg msgs[2];

    msgs[0].addr = addr;       /* Slave address */
    msgs[0].flags = RT_I2C_WR; /* Write flag */
    msgs[0].buf = &tmp;        /* Slave register address */
    msgs[0].len = 1;           /* Number of bytes sent */

    msgs[1].addr = addr;                         /* Slave address */
    msgs[1].flags = RT_I2C_WR | RT_I2C_NO_START; /* Read flag */
    msgs[1].buf = data;                          /* Read data pointer */
    msgs[1].len = len;                           /* Number of bytes read */

    if (rt_i2c_transfer(bus, msgs, 2) != 2)
    {
        return -RT_ERROR;
    }

    return RT_EOK;
}

static int8_t _i2c_read_reg(void *bus, uint8_t addr, uint8_t reg, uint8_t *data, uint16_t len)
{
    if (bus == RT_NULL)
    {
        rt_kprintf("error, bus not exist\n");
        return RT_ERROR;
    }

    rt_uint8_t tmp = reg;
    struct rt_i2c_msg msgs[2];

    msgs[0].addr = addr;       /* Slave address */
    msgs[0].flags = RT_I2C_WR; /* Write flag */
    msgs[0].buf = &tmp;        /* Slave register address */
    msgs[0].len = 1;           /* Number of bytes sent */

    msgs[1].addr = addr;       /* Slave address */
    msgs[1].flags = RT_I2C_RD; /* Read flag */
    msgs[1].buf = data;        /* Read data pointer */
    msgs[1].len = len;         /* Number of bytes read */

    if (rt_i2c_transfer(bus, msgs, 2) != 2)
    {
        return -RT_ERROR;
    }

    return RT_EOK;
}

static int8_t rt_spi_write_reg(void *bus, uint8_t addr, uint8_t reg, uint8_t *data, uint16_t len)
{

    rt_err_t write_result = rt_spi_send_then_send(&bmm150_spi_dev, &reg, 1, data, len);
    return (int8_t)write_result;
}

static int8_t _spi_read_reg(void *bus, uint8_t addr, uint8_t reg, uint8_t *data, uint16_t len)
{
    rt_err_t read_result = rt_spi_send_then_recv(&bmm150_spi_dev, &reg, 1, data, len);
    return (int8_t)read_result;
}

static struct bmm150_dev *_bmm150_create(struct rt_sensor_intf *intf, uint8_t spi_i2c)
{
    struct bmm150_dev *bmm150 = RT_NULL;
    struct rt_spi_device *spi_dev = RT_NULL;
    int8_t rslt = BMM150_OK;

    if (spi_i2c == 0)
    {
        spi_dev = (struct rt_spi_device *)rt_device_find(intf->dev_name);
        if (spi_dev == RT_NULL)
        {
            LOG_E("can not find device %s", intf->dev_name);
            return RT_NULL;
        }

        // 配置SPI传输参数
        struct rt_spi_configuration cfg;
        cfg.data_width = 8;
        cfg.mode = RT_SPI_MASTER | RT_SPI_MODE_0 | RT_SPI_MSB;
        cfg.max_hz = 5 * 1000 * 1000; /* 20M */

        if (rt_spi_configure(spi_dev, &cfg) != RT_EOK)
        {
            rt_kprintf("bmm150 config spi failed\n");
            return RT_NULL;
        }
    }
    bmm150_spi_dev = *spi_dev;
    bmm150 = rt_calloc(1, sizeof(struct bmm150_dev));
    if (bmm150 == RT_NULL)
    {
        LOG_E("bmm150 dev memory allocation failed");
        return RT_NULL;
    }

    bmm150->read = spi_i2c == 0 ? _spi_read_reg : _i2c_read_reg;
    bmm150->write = spi_i2c == 0 ? rt_spi_write_reg : rt_i2c_write_reg;
    bmm150->dev_id = BMM150_DEFAULT_I2C_ADDRESS;
    bmm150->delay_ms = _delay_ms;
    bmm150->intf = spi_i2c == 0 ? BMM150_SPI_INTF : BMM150_I2C_INTF;

    rslt = bmm150_init(bmm150); // 使用SPI总线探测bmm150，具体地就是读地址寄存器，看是否满足要求

    if (rslt == BMM150_OK) // 读取成功之后，设置bmi160设备加速度和陀螺仪相关配置
    {
        _fifo_init(bmm150); // 申请fifo信息结构体，并赋予它一块可使用（监测）的内存块

        if (bmm150->chip_id == BMM150_CHIP_ID) /* BMM150 */
        {
            rslt = bmm150_soft_reset(bmm150);

            /* Set the power mode and preset mode to enable Mag data sampling */
            bmm150->settings.pwr_mode = BMM150_NORMAL_MODE;
            bmm150_set_op_mode(bmm150);

            bmm150->settings.preset_mode = BMM150_PRESETMODE_LOWPOWER;
            bmm150_set_presetmode(bmm150);
        }
        rt_kprintf("bmm150 init success\n");
        return bmm150;
    }
    else
    {
        rt_kprintf("bmm150 init failed\n");
        rt_free(bmm150);
        return RT_NULL;
    }
}

static rt_err_t _set_odr(rt_sensor_t sensor, rt_uint16_t odr)
{
    struct bmm150_dev *bmm150 = sensor->parent.user_data;

    if (sensor->info.type == RT_SENSOR_CLASS_MAG)
    {
        const static struct odr_node tab[] =
            {
                {2, BMM150_DATA_RATE_02HZ},
                {6, BMM150_DATA_RATE_06HZ},
                {8, BMM150_DATA_RATE_08HZ},
                {10, BMM150_DATA_RATE_10HZ},
                {15, BMM150_DATA_RATE_15HZ},
                {20, BMM150_DATA_RATE_20HZ},
                {25, BMM150_DATA_RATE_25HZ},
                {0, BMM150_DATA_RATE_30HZ},
            };

        bmm150->settings.data_rate = range_find_mask(tab, odr);
        bmm150_set_sensor_settings(BMM150_DATA_RATE_SEL, bmm150); // 更新地磁输出ODR
    }

    return RT_EOK;
}

// 方式和_set_odr相同，都是查表然后更新配置参数，这里是3个输出的范围
static rt_err_t _set_range(rt_sensor_t sensor, rt_uint16_t range)
{

    return RT_ERROR;
}

// RTT应该调用此函数，来对bmi160和bmm150进行上电控制，此函数封装了bmi160和bmm150内部的上电控制函数
static rt_err_t _set_power(rt_sensor_t sensor, rt_uint8_t power)
{
    int idx;
    struct bmm150_dev *bmm150 = sensor->parent.user_data;
    uint8_t *const pwr_reg[1] =
        {
            &bmm150->settings.pwr_mode};
    const uint8_t pwr_cfg[3] =
        {
            BMM150_SUSPEND_MODE,
            BMM150_NORMAL_MODE,
            BMM150_FORCED_MODE,
        };

    switch (sensor->info.type)
    {
    case RT_SENSOR_CLASS_MAG:
        idx = 0;
        break;
    default:
        LOG_W("Unsupported sensor type, type is %d", sensor->info.type);
        return -RT_ERROR;
    }

    switch (power)
    {
    case RT_SENSOR_POWER_DOWN: /* power down */
        *pwr_reg[idx] = pwr_cfg[0];
        break;
    case RT_SENSOR_POWER_NORMAL: /* power normal */
        *pwr_reg[idx] = pwr_cfg[1];
        break;
    case RT_SENSOR_POWER_LOW: /* power low */
        *pwr_reg[idx] = pwr_cfg[2];
        break;
    default:
        LOG_W("Unsupported mode, code is %d", power);
        return -RT_ERROR;
    }

    bmm150_set_op_mode(bmm150); // bmm150设备供电模式不一定更改，但为了统一性还是写在这里

    return RT_EOK;
}

static void _get_mag_data(struct bmm150_dev *bmm150, struct rt_sensor_data *data)
{
    bmm150_read_mag_data(bmm150);
    data->data.mag.x = bmm150->data.x;
    data->data.mag.y = bmm150->data.y;
    data->data.mag.z = bmm150->data.z;
    data->timestamp = rt_sensor_get_ts();
}

static void *_mag_init(const char *name, struct rt_sensor_config *cfg, void *hdev, uint8_t spi_i2c)
{
    rt_int8_t result;
    rt_sensor_t sensor_mag = RT_NULL;

    sensor_mag = rt_calloc(1, sizeof(struct rt_sensor_device));
    if (sensor_mag == RT_NULL)
        return RT_NULL;

    sensor_mag->info.type = RT_SENSOR_CLASS_MAG;
    sensor_mag->info.vendor = RT_SENSOR_VENDOR_BOSCH;
    sensor_mag->info.model = "bmm150";
    sensor_mag->info.unit = RT_SENSOR_UNIT_MGAUSS;
    sensor_mag->info.intf_type = spi_i2c == 0 ? RT_SENSOR_INTF_SPI : RT_SENSOR_INTF_I2C;
    sensor_mag->info.range_max = 2000;
    sensor_mag->info.range_min = 125;
    sensor_mag->info.period_min = 100;
    sensor_mag->info.fifo_max = FIFO_DATA_LEN;

    rt_memcpy(&sensor_mag->config, cfg, sizeof(struct rt_sensor_config));
    sensor_mag->ops = &sensor_ops;
    rt_kprintf("bmm150 before rt_hw_sensor_register\n");

    result = rt_hw_sensor_register(sensor_mag, name, RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX | RT_DEVICE_FLAG_FIFO_RX, hdev);
    if (result != RT_EOK)
    {
        LOG_E("device register err code: %d", result);
        rt_free(sensor_mag);
        rt_device_unregister(&sensor_mag->parent);
        return RT_NULL;
    }

    rt_kprintf("bmm150 register success\n");

    return sensor_mag;
}

static int _get_data(struct rt_sensor_device *sensor, void *buf)
{
    struct bmm150_dev *bmm150 = sensor->parent.user_data;
    struct rt_sensor_data *data = buf;

    if (sensor->info.type == RT_SENSOR_CLASS_MAG)
    {
        if (bmm150->chip_id == BMM150_CHIP_ID)
        {
            _get_mag_data(bmm150, data);
            return 1;
        }
        return 0;
    }
    return 1;
}

static int _get_fifo_data(struct rt_sensor_device *sensor, void *buf, rt_size_t len)
{
    return 0;
}

static rt_size_t _fetch_data(struct rt_sensor_device *sensor, void *buf, rt_size_t len)
{
    if (sensor->config.mode == RT_SENSOR_MODE_FIFO)
        return _get_fifo_data(sensor, buf, len);
    return _get_data(sensor, buf);
}

static rt_err_t _control(struct rt_sensor_device *sensor, int cmd, void *args)
{
    struct bmm150_dev *bmm150 = sensor->parent.user_data;
    rt_err_t result = RT_EOK;

    switch (cmd)
    {
    case RT_SENSOR_CTRL_GET_ID:
        *(rt_uint8_t *)args = bmm150->chip_id;
        break;
    case RT_SENSOR_CTRL_SET_ODR:
        result = _set_odr(sensor, (rt_uint32_t)args & 0xffff);
        break;
    case RT_SENSOR_CTRL_SET_RANGE:
        result = _set_range(sensor, (rt_uint32_t)args);
        break;
    case RT_SENSOR_CTRL_SET_POWER:
        result = _set_power(sensor, (rt_uint32_t)args & 0xff);
        break;
    case RT_SENSOR_CTRL_SET_MODE:
        result = -RT_EINVAL;
        break;
    case RT_SENSOR_CTRL_SELF_TEST:
        /* TODO */
        result = -RT_EINVAL;
        break;
    default:
        return -RT_EINVAL;
    }
    return result;
}

int rt_hw_bmm150_init(const char *name, struct rt_sensor_config *cfg, uint8_t spi_i2c)
{
    struct bmm150_dev *hdev = RT_NULL;

    hdev = _bmm150_create(&cfg->intf, spi_i2c);

    if (hdev == RT_NULL)
    {
        return -RT_ERROR;
    }

    _mag_init(name, cfg, hdev, spi_i2c);

    return RT_EOK;
}
